import redis, os, json, re
from typing import List, Optional

import uuid


class MyRedis:
    def __init__(self, max_connections=30):
        redis_link = self.__query_redis_link__()
        self.pool = redis.ConnectionPool(
            host=redis_link["HOST"],
            port=redis_link["PORT"],
            db=0,
            password=redis_link["PASSWORD"],
            max_connections=max_connections
        )
        self.r = redis.Redis(connection_pool=self.pool)

    def __query_redis_link__(self):
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        config_file = os.path.join(BASE_DIR, 'SaaSDeveMySqlConfig', 'database.json')
        with open(config_file) as f:
            database_config = json.load(f)
        return database_config["redis_link"]

    def select(self, db: int):
        """
        修改当前行动库
        :param db:
        :return:
        """
        self.r.select(db)

    def find_all_keys(self, db: int):
        self.r.select(db)
        return [i.decode() for i in self.r.keys()]

    def list_getall(self, db: int, key: str, start: int, end: int) -> List[bytes]:
        """
        获取指定范围内的所有元素
        """
        self.select(db)
        data_list = self.r.lrange(key, start, end)
        return [json.loads(data.decode('utf-8')) for data in data_list]

    def list_rpush(self, db: int, key: str, val) -> None:
        """
        将元素从右侧插入列表
        """
        self.select(db)
        self.r.rpush(key, val)

    def insert_set(self, db: int, key: str, val) -> None:
        """
        将元素插入集合
        """
        self.select(db)
        self.r.sadd(key, val)

    def find_set(self, db: int, key: str) -> Optional[List[str]]:
        """
        查找集合中的所有元素
        """
        self.select(db)
        val = self.r.smembers(key)
        if val:
            return [str(i, encoding='utf8') for i in val]
        else:
            return None

    def insert_hash(self, db: int, key: str, params, value) -> None:
        """
        在哈希表中插入键值对
        """
        self.select(db)
        self.r.hset(key, params, value)

    def get_value_hash(self, db: int, key: str, params) -> Optional[bytes]:
        """
        获取哈希表中指定键的值
        """
        self.select(db)
        return self.r.hget(key, params)

    def get_all_hash(self, db: int, key: str) -> dict:
        """
        获取哈希表中的所有键值对
        """
        self.select(db)
        return self.r.hgetall(key)

    def del_hash(self, db: int, key: str, params) -> int:
        """
        删除哈希表中的指定键
        """
        self.select(db)
        return self.r.hdel(key, params)

    def set_str(self, db: int, key: str, value, time: int = 0) -> None:
        """
        设置字符串类型的键值对
        """
        self.select(db)
        if time > 0:
            self.r.setex(key, time, value)
        else:
            self.r.set(key, value)

    def get_str(self, db: int, key: str) -> str:
        """
        获取字符串类型的值
        """
        self.select(db)
        data = self.r.get(key)
        if data:
            data = data.decode()
        return data

    def del_str(self, db: int, key: str) -> int:
        """
        删除字符串类型的键值对
        """
        self.select(db)
        if self.r.delete(key):
            return True
        else:
            return False

    def list_push(self, db: int, key: str, val) -> None:
        """
        将元素从左侧插入列表
        """
        self.select(db)
        self.r.lpush(key, val)

    def list_pop(self, db: int, key: str) -> Optional[bytes]:
        """
        从列表中弹出最右侧的元素
        """
        self.select(db)
        return self.r.rpop(key)

    def list_len(self, db: int, key: str) -> int:
        """
        获取列表的长度
        """
        self.select(db)
        if self.r.exists(key):
            return self.r.llen(key)
        else:
            return 0

    def sortedset_add(self, db: int, pkey: str, key: str, score) -> None:
        """
        向有序集合中添加元素
        """
        self.select(db)
        mapping = {key: score}
        self.r.zadd(pkey, mapping)

    def sortedset_getall(self, db: int, pkey: str) -> List[bytes]:
        """
        获取有序集合中的所有元素
        """
        self.select(db)
        return self.r.zrange(pkey, 0, -1)

    def sortedset_getbyscore(self, db: int, pkey: str, min, max) -> List[bytes]:
        """
        根据分数范围获取有序集合中的元素
        """
        self.select(db)
        return self.r.zrangebyscore(pkey, min, max)

    def sortedset_delete(self, db: int, pkey: str, key: str) -> None:
        """
        从有序集合中删除指定的元素
        """
        self.select(db)
        self.r.zrem(pkey, key)

    def list_lpop(self, db: int, key: str) -> Optional[bytes]:
        """
        从列表中弹出最左侧的元素
        """
        self.select(db)
        return self.r.lpop(key)

    def clear_all_data(self, db: int):
        """
        清空所有数据
        """
        self.select(db)
        self.r.flushall()


class redis_cache:
    """
    保存在第0库中
    (db)(拼接key的方式)(time)
    (4)(YHZ_{0}_{1})(123)
    """

    def __init__(self, MyRedis):
        self.r = MyRedis

    # 拼接redis中保存的缓存key值
    def __get_redis_formula__(self, find_name: str, insert_list: list):
        """
        拼接redis中保存的缓存key值
        1.找到数据库0中的配置信息
        2.拼接配置信息
        3.通过拼接的key值找到缓存
        :param find_name:模板名称
        :param insert_list:模板插入数据
        :return: 数据库号，拼接后的key值，过期时间
        """
        # 1.找到数据库0中的配置信息
        formula = r.get_str(0, find_name)
        if formula:  # 查看有没有此项配置
            self.checking_formula(formula, insert_list)  # 检查插入列表是否合规`
            # 2.拼接配置信息
            formula = formula.format(*insert_list)
            re_str = r'\((.*?)\)'
            tear_data = re.findall(re_str, formula)
            db = tear_data[0]  # redis数据库
            r_key = tear_data[1]  # key值
            time = tear_data[2]  # 过期时间
            if time == 0:
                raise Exception('保存时间值不允许为0')
            return (db, r_key, time)
        else:
            raise Exception('没有此缓存模板名')

    # 检查模板与插入的字符串数量是否匹配
    def checking_formula(self, formula, insert_list):
        """
        检查模板与插入的字符串数量是否匹配
        :param formula:模板字符串
        :param insert_list:插入数据列表
        :return:
        """
        rel = r'\{(.*?)\}'
        find_num = re.findall(rel, formula)
        if len(find_num) == len(insert_list):
            return
        else:
            raise Exception('插入数据数量不符')

    # 获取redis中保存的缓存
    def find(self, find_name: str, insert_list: list) -> str:
        """
        获取redis中保存的缓存
        :param find_name:模板名称
        :param insert_list:模板插入数据
        :return:
        """
        find_formula = self.__get_redis_formula__(find_name, insert_list)
        if find_formula:
            data = r.get_str(find_formula[0], find_formula[1])
            return data
        else:
            return None

    # 写入redis中保存的缓存
    def write(self, find_name: str, insert_list: list, data: str) -> str:
        """
        写入redis缓存
        :param find_name: 模板名称
        :param insert_list: 模板插入数据
        :param data: 保存的数据 josn格式
        :return:
        """
        find_formula = self.__get_redis_formula__(find_name, insert_list)
        if find_formula:
            r.set_str(find_formula[0], find_formula[1], data, time=int(find_formula[2]))
        else:
            return None

    # 删除redis中保存的缓存
    def delete(self, find_name: str, insert_list: list) -> str:
        """
        删除redis中保存的缓存
        :param find_name:模板名称
        :param insert_list:模板插入数据
        :return:
        """
        find_formula = self.__get_redis_formula__(find_name, insert_list)
        if find_formula:
            data = r.del_str(find_formula[0], find_formula[1])
            return data
        else:
            return None


class redis_celery:

    def __init__(self, MyRedis):
        self.r = MyRedis

    def add_task(self, func_name: str, add_data_dict: dict):
        """
        添加任务
        :param func_name: celery中运行函数的名字
        :param add_data_dict:
        :return:
        """
        basic_uuid = 'add_' + str(uuid.uuid4())
        val = json.dumps({"fun_name": func_name, "val": add_data_dict})
        self.r.set_str(28, basic_uuid, val)


r = MyRedis()  # 连接redis
cache = redis_cache(r)  # 缓存
r_celery = redis_celery(r)  # 添加celery
